#include "pubsub.h"

#include <QDebug>
#include <QMetaObject>
#include <QMetaMethod>

PubSub::PubSub(QObject *parent)
    : QObject{parent}
{
    defaultAdapter = new PSAdapter(this);
    for(int topic = 0; topic < Topic_Max; topic++) {
        adapters[topic] = new PSAdapter(this);
    }

   // subscribe(Topic_Test, this, SLOT(onTest));
}

PubSub &PubSub::getInstance()
{
    static PubSub pubsub;
    return pubsub;
}

PSAdapter *PubSub::adapter(int topic) const
{
    if(topic < 0 || topic >= Topic_Max) {
        qWarning() << "Unknown Topic:" << topic;
        return defaultAdapter;
    }

    return adapters[topic];
}

void PubSub::subscribe(int topic, const QObject *subscriber, const char *slotName, Qt::ConnectionType connectionType)
{
    QByteArray slot = QByteArray(slotName);

    if(!slot.contains('(')) {
        slot += "(int,QVariant)";
    }

    if(connect(adapter(topic), SIGNAL(notify(int,QVariant)), subscriber, slot, connectionType)) {
        mutex.lock();
        subscribers << SubscriberInfo(topic, subscriber, slot);
        mutex.unlock();
    } else {
        qWarning() << "fail to subscribe topic:" << topic;
    }
}

void PubSub::unsubscribe(int topic, const QObject *subscriber)
{
    mutex.lock();
    for(auto &info: subscribers) {
        if(info.topic == topic && info.subscriber == subscriber) {
            if(disconnect(adapter(topic), SIGNAL(notify(int,QVariant)), subscriber, info.slot)) {
                subscribers.removeOne(info);
            } else {
                qWarning() << "fail to unsubscribe topic:" << topic;
            }
        }
    }
    mutex.unlock();
}

void PubSub::unsubscribe(const QObject *subscriber)
{
    mutex.lock();
    for(auto &info: subscribers) {
        if(info.subscriber == subscriber) {
            if(disconnect(adapter(info.topic), SIGNAL(notify(int,QVariant)), subscriber, info.slot)) {
                subscribers.removeOne(info);
            } else {
                qWarning() << "fail to unsubscribe subscriber:" << subscriber;
            }
        }
    }
    mutex.unlock();
}

void PubSub::publish(int topic, QVariant arg)
{
    emit adapter(topic)->notify(topic, arg);
}

void PubSub::onTest(int topic, QVariant arg)
{
    qDebug() << "PubSub notified:" << topic << arg;
}
